import re
from django.shortcuts import *
from django.views import View
from django.http.response import *
from hopublog.settings import MEDIA_URL, BASE_DIR
from libs.captcha.captcha import captcha # 导入验证码
from django_redis import get_redis_connection # 导入reids
from django.http.response import JsonResponse # 导入Json返回值包
from utils.response_code import RETCODE # 导入自定义相应码
import logging # 导入loggin包
from random import randint # 导入随机数包
from libs.yuntongxun.sms import CCP # 导入容联云包
from django.contrib.auth import *
from django.contrib.auth.mixins import LoginRequiredMixin
from home.models import *
import json
logger = logging.getLogger('django_log')
# 注册视图
class RegisterView(View):
    def get(self,request):
        return render(request,'register.html')


    def post(self,request):
        # 1,接受用户数据
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        sms_code = request.POST.get('sms_code')
        #2 验证数据
        #2-1 参数是否齐全
        if not all([mobile,password,password2,sms_code]):
            return HttpResponseBadRequest('参数不全')
        # 2-2、手机号格式是否正确
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('手机号码格式不正确')
        # 2-3、密码是否符合格式
        if not re.match(r'^[0-9a-zA-Z]{8,20}$', password) or not re.match(r'^[0-9a-zA-Z]{8,20}$', password2):
            return HttpResponseBadRequest('密码长度、格式不正确。长度8-20，必须是数字、字母')
        # 2-4、密码和确认密码是否一致
        if password != password2:
            return HttpResponseBadRequest('两次输入的密码不一致')
        # 获取redis中的短信验证码
        redis_conn = get_redis_connection()
        redis_sms_code =redis_conn.get('sms:%s' % mobile)
        if redis_sms_code.decode() != sms_code:
            return HttpResponseBadRequest('短信验证码错误')
        # 3保存注册信息
        user = User.objects.create_user(username=mobile,mobile=mobile,password=password)
        login(request,user)
        resp = redirect(reverse('home:index'))
        resp.set_cookie('is_login',True)
        resp.set_cookie('login_name',user.username)
        return resp
# 验证码视图
class ImageView(View):
    def get(self,request):
        '''
        步骤：
        1、接收前端传递过来的uuid
        2、判断uuid失分获取到
        3、通过调用captcha来生成图片验证码（返回：图片内容和图片二进制）
        4、将图片内容保存到redis中。uuid作为key，图片内容作为值，同时还需要设置一个过期时间
        5、返回图片给前端:
        param request:
        :return:
        '''
        # 1、接收前端传递过来的uuid
        uuid = request.GET.get('uuid')
        # 2、判断uuid失分获取到
        if uuid is None:
            return HttpResponseBadRequest('没有获取到uuid')
        # 3、通过调用captcha来生成图片验证码（返回：图片内容和图片二进制）
        txt, image = captcha.generate_captcha()
        # 4、将图片内容保存到redis中。uuid作为key，图片内容作为值，同时还需要设置一个过期时间
        redis_conn = get_redis_connection('default')
        # name:数据key，这里采用img前缀：uuid
        # time:300秒后过期
        # value：对应key的值
        redis_conn.setex(name='img:%s' % uuid, time=300, value=txt)
        # 5、返回图片给前端:
        return HttpResponse(image, content_type='image/jpeg')
# 短信验证视图
class SmsCodeView(View):
    def get(self, request):
        '''
            实现思路：
            1、接收参数
            2、参数验证
            2.1 验证参数是否齐全
            2.2 图片验证码的验证
            连接reids，获取reids中图片验证码
            判断图片验证码是否存在
            如果图片验证码未过期，我们获取到之后就可以删除图片验证码
            比对图片验证码（忽略用户大小写）
            3、生成短信验证码
            4、保存短信验证码内容到redis中
            5、发送短信
            6、返回响应信息
            :param request:
            :return:
        '''
        # 1、接收参数
        mobile = request.GET.get('mobile')
        image_code = request.GET.get('image_code')
        uuid = request.GET.get('uuid')
        # 2、参数验证
        # 2.1
        # 验证参数是否齐全
        if not all([mobile, image_code, uuid]):
            return JsonResponse({'code': RETCODE.NECESSARYPARAMERR, 'errmsg': '缺少必要的参数'})
        # 2.2
        # 图片验证码的验证
        # 连接reids，获取reids中图片验证码
        # 创建reids连接对象
        redis_conn = get_redis_connection()
        # 根据传来的图片uuid，获取reids中的值
        redis_image_code = redis_conn.get('img:%s' % uuid)
        # 判断图片验证码是否存在
        if redis_image_code is None:
            return JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图片验证码已过期'})
        # 如果图片验证码未过期，我们获取到之后就可以删除图片验证码
        # 删除图形验证码，避免恶意测试图形验证码
        redis_conn.delete('img:%s' % uuid)
        # 比对图片验证码（全部转为小写） redis中的数据是bytes类型
        if redis_image_code.decode().lower() != image_code.lower():
            return JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图片验证码错误'})
        # 3、生成短信验证码：生成6位数验证码
        sms_code = '%06d' % randint(0, 999999)
        # 为了后期测试方便，我们可以把短信验证码记录到日志中
        logger.info(f'短信验证码：{sms_code}')
        # 4、保存短信验证码内容到redis中,保存时间1分钟
        redis_conn.setex('sms:%s' % mobile, 60, sms_code)
        # 5、发送短信
        ccp = CCP()
        ccp.send_template_sms('13992143908', [sms_code, 5], 1)
        print(f'您的验证码是: {sms_code}')
        # 6、返回响应信息
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '短信发送成功'})
# 登录视图
class LoginView(View):
    '''
           实现思路：
           1、接收提交参数
           2、验证参数
            2-1、手机号码是否符合规则
            2-2、密码是否符合规则
           3、用户认证登录
           4、状态保持
           5、根据用户选择的是否记住登录状态进行判断
           6、设置cookie信息，为首页显示服务
           7、跳转到首页
           :param request:
           :return:
           '''
    def get(self,req):
        return render(req,'login.html')
    def post(self,req):
        # 1、接收提交参数
        mobile = req.POST.get('mobile')
        password = req.POST.get('password')
        remember = req.POST.get('remember')
        # 2、验证参数
        if not all([mobile,password]):
            return render(req,'login.html',{'msg':'参数不齐全'})
        # 2 - 1、手机号码是否符合规则
        if not re.match('^1[3-9]\d{9}$',mobile):
            return render(req, 'login.html', {'msg':'手机号码格式不正确'})
        # 2 - 2、密码是否符合规则
        if not re.match('^[a-z0-9A-Z]{8,20}$',mobile):
            return render(req, 'login.html', {'msg': '密码格式不正确'})
        # 3、用户认证登录
        return_user = authenticate(mobile=mobile,password=password)
        if return_user is None:
            return render(req,'login.html',{'msg':'账号或密码错误'})
        # 4、状态保持
        login(req,return_user)

        # resp = redirect(reverse('home:index'))
        next_page = req.GET.get('next')
        if next_page:
            resp = redirect(next_page)
        else:
            resp = redirect(reverse('home:index'))
        # 5、根据用户选择的是否记住登录状态进行判断
        if remember != 'on': # 用户为勾选
            req.session.set_expiry(0) # 当浏览器关闭后，清空session
            resp.set_cookie('is_login',True)
            username = json.dumps(return_user.username)
            resp.set_cookie('login_name',username)
        else:# 用户勾选
            req.session.set_expiry(None)# 设置session的过期时间为默认值，这个默认时间是2周
            resp.set_cookie('is_login', True,max_age=14 * 24 * 3600)
            username = json.dumps(return_user.username)
            resp.set_cookie('login_name', username,max_age=14 * 24 * 3600)
        return resp
#退出登录视图
class LogoutView(View):
    def get(self,req):
        # 1,清除session数据
        logout(req)
        resp = redirect(reverse('home:index'))
        # 2,删除cookiee数据
        resp.delete_cookie('is_login')
        resp.delete_cookie('login_name')
        # 3,跳转到首页
        return resp
# 忘记密码视图
class ForgetPasswordView(View):
    def get(self, request):
        return render(request, 'forget_password.html')

    def post(self, request):
        '''
        实现思路：
        1、接收数据
        2、验证数据
            2-1、判断参数是否齐全
            2-2、手机号是否符合规则
            2-3、判断密码是否符合规则
            2-4、判断确认密码是否一致
            2-5、判断短信验证码是否正确
        3、根据手机号码进行用户信息的查询
        4、如果手机号查询出用户信息则进行用户密码的修改
        5、如果手机号没有查询出用户信息，则进行新用户的创建
        6、进行页面跳转，跳转到登录页面
        7、返回响应
        :param request:
        :return:
        '''
        # 1、接收数据
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        sms_code = request.POST.get('sms_code')
        # 2、验证数据
        #     2-1、判断参数是否齐全
        if not all([mobile, password, password2, sms_code]):
            return HttpResponseBadRequest('缺少必要的参数')
        #     2-2、手机号是否符合规则
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('手机格式不正确')
        #     2-3、判断密码是否符合规则
        if not re.match(r'^[0-9A-Za-z]{8,20}$', password):
            return HttpResponseBadRequest('密码格式不正确')
        #     2-4、判断确认密码是否一致
        if password != password2:
            return HttpResponseBadRequest('两次密码输入不一致')
        #     2-5、判断短信验证码是否正确
        redis_conn = get_redis_connection('default')
        redis_sms_code = redis_conn.get('sms:%s' % mobile)
        if redis_sms_code is None:
            return HttpResponseBadRequest('短信验证码过期')
        try:
            if redis_sms_code.decode() != sms_code:
                return HttpResponseBadRequest('验证码错误')
        except Exception as e:
            logger.error(e)
            return HttpResponseBadRequest('验证码错误')
        # 3、根据手机号码进行用户信息的查询
        return_user = User.objects.filter(mobile=mobile).first()
        # 4、如果手机号查询出用户信息则进行用户密码的修改
        # 5、如果手机号没有查询出用户信息，则进行新用户的创建
        if return_user is None:
            try:
                User.objects.create_user(username=mobile, mobile=mobile, password=password)
            except Exception as e:
                logger.error(e)
                return HttpResponseBadRequest('注册失败')
        else:
            try:
                # 调用系统user对象的set_password()进行修改密码，该方法会对密码进行加密
                return_user.set_password(password)
                return_user.save()
            except Exception as e:
                logger.error(e)
                return HttpResponseBadRequest('修改密码失败')
        # 6、进行页面跳转，跳转到登录页面
        resp = redirect(reverse('users:login'))
        # 7、返回响应
        return resp
# 用户中心视图
class UserCenterView(LoginRequiredMixin,View):
    def get(self, req):
        userinfo = req.user
        context = {
            'username': userinfo.username,
            'mobile': userinfo.mobile,
            'avatar': userinfo.avatar.url if userinfo.avatar else None,
            'user_desc': userinfo.user_desc
        }
        return render(req, 'usercenter.html',context=context)
    def post(self,req):
        username = req.POST.get('username')
        desc = req.POST.get('desc')
        avatar = req.FILES.get('avatar')
        userinfo = req.user # 获取到已登录的用户信息,并且这个用户是被orm跟踪
        userinfo.username = username
        userinfo.user_desc = desc
        userinfo.avatar = avatar
        userinfo.save() # 更新到数据库
        username = json.dumps(userinfo.username)
        resp = redirect(reverse('users:usercenter'))
        resp.set_cookie('login_name', username)
        return resp
# 写博客视图
class WriteBlogView(LoginRequiredMixin,View):
    def get(self,req):
        categories = ArticleCategory.objects.all()
        context = {
            'categories':categories
        }
        return render(req,'writeblog.html',context=context)
    def post(self, req):
        '''
        实现思路：
        1、接收数据
        2、验证数据
        3、数据入库
        4、跳转到指定页面
        :param request:
        :return:
        '''
        # 1、接收数据
        avatar = req.FILES.get('avatar')

        title = req.POST.get('title')
        category_id = req.POST.get('category')
        tags = req.POST.get('tags')
        sumary = req.POST.get('sumary')
        content = req.POST.get('content')
        user = req.user
        if avatar is None:
            avatar = req.FILES.get('image')
            with open('media/' + avatar.name, 'wb') as f:
                for line in avatar:
                    f.write(line)
            # print('------image=', avatar.name, '------------')
            try:
                data = {
                    'state': 1,
                    'url': '/media/' + avatar.name
                }
                return JsonResponse(data)
            except:
                data = {'state': 0}
                return JsonResponse(data)

        # WIN_20211011_16_46_18_Pro.jpg

        # 2、验证数据
        # 2-1、参数齐全验证
        if not all([avatar, title, category_id, sumary, content]):
            return HttpResponseBadRequest('参数不齐全')
        # 2-2、判断分类id
        try:
            category = ArticleCategory.objects.filter(pk=category_id).first()
        except ArticleCategory.DoesNotExist:
            return HttpResponseBadRequest('没有该分类信息')
        # 3、数据入库
        try:
            Article.objects.create(
                author=user,
                avatar=avatar,
                title=title,
                category=category,
                tags=tags,
                sumary=sumary,
                content=content
            )
            os.remove(BASE_DIR + MEDIA_URL + avatar.name)
        except Exception as e:
            logger.error(e)
            return HttpResponseBadRequest('发布失败，请稍后重试')
        # 4、跳转到指定页面
        return redirect(reverse('home:index'))
    